#include "Frustrum.h"

/// <Summary> The collision type of this object </Summary>
const HashString Frustrum::COLLISIONTYPE("COLLISION_FRUSTRUM");

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Transforms this object by a matrix </Summary>
///
/// <Param name="transform">	The transform matrix	</Param>
////////////////////////////////////////////////////////////////////////////////////////////////////
void Frustrum::Transform(const Matrix4x4& transform)
{
	for(unsigned int i = 0; i < 6; ++i)
	{
		planes[i].Transform( transform );
	}

	for(unsigned int i = 0; i < 8; ++i)
	{
		Vector3 newPoint;
		TranslateVector( newPoint, points[i], transform );
		points[i] = newPoint;
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Builds the frustrum </Summary>
///
/// <Param name="fov">	   	The field of view </Param>
/// <Param name="width">   	The width </Param>
/// <Param name="height">  	The height </Param>
/// <Param name="nearDist">	The near distance </Param>
/// <Param name="farDist"> 	The far distance </Param>
////////////////////////////////////////////////////////////////////////////////////////////////////
void Frustrum::Build(float fov, float width, float height, float nearDist, float farDist)
{
	float ratio = width / height;
	float Hnear = 2.0f * tanf(fov/2.0f) * nearDist;
	float Wnear = Hnear * ratio;
	float Hfar = 2.0f * tanf(fov/2.0f) * farDist;
	float Wfar = Hfar * ratio;

	Vector3 up(0.0f, 1.0f, 0.0f);
	Vector3 right(1.0f, 0.0f, 0.0f);

	Vector3 fc(0.0f, 0.0f, farDist);

	Vector3 ftl = fc + (up * Hfar*0.5) - (right * Wfar*0.5f);
	Vector3 ftr = fc + (up * Hfar*0.5) + (right * Wfar*0.5f);
	Vector3 fbl = fc - (up * Hfar*0.5) - (right * Wfar*0.5f);
	Vector3 fbr = fc - (up * Hfar*0.5) + (right * Wfar*0.5f);

	Vector3 nc(0.0f, 0.0f, nearDist);

	Vector3 ntl = nc + (up * Hnear*0.5) - (right * Wnear*0.5f);
	Vector3 ntr = nc + (up * Hnear*0.5) + (right * Wnear*0.5f);
	Vector3 nbl = nc - (up * Hnear*0.5) - (right * Wnear*0.5f);
	Vector3 nbr = nc - (up * Hnear*0.5) + (right * Wnear*0.5f);

	points[0] = ftl;
	points[1] = ftr, 
		points[2] = fbl;
	points[3] = fbr;

	points[4] = ntl;
	points[5] = ntr;
	points[6] = nbl;
	points[7] = nbr;

	planes[0].normal = Vector3(0.0f, 0.0f, 1.0f);
	planes[0].offset = nearDist;
	planes[1].normal = Vector3(0.0f, 0.0f, -1.0f);
	planes[1].offset = -farDist;

	CrossProduct( planes[2].normal, ntl - nbl, fbl - nbl );
	planes[2].normal.Normalize();
	planes[2].offset = DotProduct( planes[2].normal, nbl );

	CrossProduct( planes[3].normal, fbr - nbr, ntr - nbr );
	planes[3].normal.Normalize();
	planes[3].offset = DotProduct( planes[3].normal, nbr );

	CrossProduct( planes[4].normal, ftr - ntr, ntl - ntr );
	planes[4].normal.Normalize();
	planes[4].offset = DotProduct( planes[4].normal, ntr );

	CrossProduct( planes[5].normal, fbl - nbl, nbr - nbl );
	planes[5].normal.Normalize();
	planes[5].offset = DotProduct( planes[5].normal, nbl );
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Prints this object to the console </Summary>
///
/// <Param name="out">	The console window </Param>
///
/// <Return>	The console window </Return>
////////////////////////////////////////////////////////////////////////////////////////////////////
ConsoleWindow& Frustrum::Print(ConsoleWindow& out) const
{
	out << "\nFRUSTRUM: ";

	for(int i = 0; i < 6; ++i)
	{
		out << "\nPlane " << i << ": " << planes[i];
	}

	for(int i = 0; i < 8; ++i)
	{
		out << "\nPoint " << i << ": " << points[i];
	}

	return out;
}